﻿/* * Write a program that calculates the value of given arithmetical expression. The expression can contain the following elements only:
Real numbers, e.g. 5, 18.33, 3.14159, 12.6
Arithmetic operators: +, -, *, / (standard priorities)
Mathematical functions: ln(x), sqrt(x), pow(x,y)
Brackets (for changing the default priorities)
	Examples:
	(3+5.3) * 2.7 - ln(22) / pow(2.2, -1.7)  ~ 10.6
	pow(2, 3.14) * (3 - (3 * sqrt(2) - 3.2) + 1.5*0.3)  ~ 21.22
	Hint: Use the classical "shunting yard" algorithm and "reverse Polish notation".
*/
using System;
using System.Text;
using System.Globalization;
using System.Threading;

namespace _7.CalcArithmeticalExpression
{
    class CalcArithmeticalExpression
    {
        static void Main()
        {
            //globalization settings
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            Console.Write("\nPlease enter an expression to be calculated\r\n\tExample: (3+5.3) * 2.7 - ln(22) / pow(2.2, -1.7) : ");
            string expr = Console.ReadLine();
            StringBuilder exprStr = new StringBuilder();
            exprStr.Append('(', 2);
            foreach (var v in expr)
            {
                if (v != ' ')
                {
                    exprStr.Append(v);
                }
            }
            exprStr.Append(')', 2);
            while (Checkforbrackets(exprStr) != 0)
            {
                exprStr = CalculateExpression(true, Checkforbrackets(exprStr), exprStr);
            }

            Console.Write("\n\nResult is: {0:0.000}\n\n", exprStr.ToString().Trim());
        }

        private static decimal ExtractNum(int offset, int spt, StringBuilder expr)
        {
            int temp = 0;
            StringBuilder result = new StringBuilder();
            spt += offset;
            if (expr[spt] == '-' || expr[spt] == '+')
            {
                result.Append(expr[spt]);
                spt += offset;
            }

            while (int.TryParse(expr[spt].ToString(), out temp) || expr[spt] == '.')
            {
                if (offset == -1)
                {
                    result.Insert(0, expr[spt]);
                }
                else
                {
                    result.Append(expr[spt]);
                }

                if (spt > 0 && spt < expr.Length - 1)
                {
                    spt += offset;
                }
                else
                {
                    break;
                }
            }

            bool check = expr[spt + offset] == '*' || expr[spt + offset] == '/' || expr[spt + offset] == '+' || expr[spt + offset] == '-';
            if (offset == -1 && ((expr[spt] == '+' && check) || (expr[spt] == '-' && check)))
            {
                result.Insert(0, expr[spt]);
            }

            return decimal.Parse(result.ToString());
        }

        //calculate a simple expression
        private static StringBuilder Calc(int start_P, StringBuilder expression)
        {
            decimal result = 0;
            switch (expression[start_P])
            {
                case '-':
                    {
                        result = ExtractNum(-1, start_P, expression) - ExtractNum(1, start_P, expression);
                        return Exchange(start_P - ExtractNum(-1, start_P, expression).ToString().Length, start_P + ExtractNum(1, start_P, expression).ToString().Length + 1, result.ToString(), expression);
                    }

                case '+':
                    {
                        result = ExtractNum(-1, start_P, expression) + ExtractNum(1, start_P, expression);
                        return Exchange(start_P - ExtractNum(-1, start_P, expression).ToString().Length, start_P + ExtractNum(1, start_P, expression).ToString().Length + 1, result.ToString(), expression);
                    }

                case '*':
                    {
                        result = ExtractNum(-1, start_P, expression) * ExtractNum(1, start_P, expression);
                        return Exchange(start_P - ExtractNum(-1, start_P, expression).ToString().Length, start_P + ExtractNum(1, start_P, expression).ToString().Length + 1, result.ToString(), expression);
                    }

                default:
                    {
                        result = ExtractNum(-1, start_P, expression) / ExtractNum(1, start_P, expression);
                        return Exchange(start_P - ExtractNum(-1, start_P, expression).ToString().Length, start_P + ExtractNum(1, start_P, expression).ToString().Length + 1, result.ToString(), expression);
                    }
            }
        }

        private static StringBuilder Exchange(int startIndex, int endIndex, string valueToExchange, StringBuilder expression)
        {
            expression.Remove(startIndex, endIndex - startIndex);
            expression.Insert(startIndex, valueToExchange);
            return expression;
        }

        //calculate a function
        private static StringBuilder Calcf(int startIndex, StringBuilder expression)
        {
            CalculateExpression(false, startIndex, expression);
            decimal result = 0;
            switch (expression[startIndex - 1])
            {
                case 'n':
                    {
                        result = (decimal)Math.Log((double)ExtractNum(1, startIndex, expression));
                        return Exchange(startIndex - 2, startIndex + ExtractNum(1, startIndex, expression).ToString().Length + 2, result.ToString(), expression);
                    }

                case 't':
                    {
                        result = (decimal)Math.Sqrt((double)ExtractNum(1, startIndex, expression));
                        return Exchange(startIndex - 4, startIndex + ExtractNum(1, startIndex, expression).ToString().Length + 2, result.ToString(), expression);
                    }

                default:
                    {
                        int startIndex2 = startIndex;
                        while (expression[startIndex2] != ',')
                        {
                            startIndex2++;
                        }

                        result = (decimal)Math.Pow((double)ExtractNum(-1, startIndex2, expression), (double)ExtractNum(1, startIndex2, expression));
                        return Exchange(startIndex - 3, startIndex + ExtractNum(-1, startIndex2, expression).ToString().Length + ExtractNum(1, startIndex2, expression).ToString().Length + 3, result.ToString(), expression);
                    }
            }
        }

        private static int Checkforbrackets(StringBuilder expression)
        {
            int index = 0;
            int result = 0;
            while (index < expression.Length && expression[index] != ')')
            {
                if (expression[index] == '(')
                {
                    result = index;
                }

                index++;
            }

            return result;
        }

        private static StringBuilder CalculateExpression(bool checkforaf, int start_I, StringBuilder expression)
        {
            if (checkforaf)
            {
                int mod = 1;
                if (start_I == 0)
                {
                    mod = 0;
                }

                if (expression[start_I - mod] == 'n' || expression[start_I - mod] == 't' || expression[start_I - mod] == 'w')
                {
                    return Calcf(start_I, expression);
                }
            }

            while (SearchSign('*', start_I, expression) != 0)
            {
                expression = Calc(SearchSign('*', start_I, expression), expression);
            }

            while (SearchSign('/', start_I, expression) != 0)
            {
                expression = Calc(SearchSign('/', start_I, expression), expression);
            }

            while (SearchSign('-', start_I, expression) != 0 || SearchSign('+', start_I, expression) != 0)
            {
                while (SearchSign('+', start_I, expression) != 0)
                {
                    if (SearchSign('-', start_I, expression) != 0 && (SearchSign('-', start_I, expression) < SearchSign('+', start_I, expression)))
                    {
                        break;
                    }

                    expression = Calc(SearchSign('+', start_I, expression), expression);
                }

                while (SearchSign('-', start_I, expression) != 0)
                {
                    expression = Calc(SearchSign('-', start_I, expression), expression);
                }
            }

            if (checkforaf)
            {
                expression.Remove(start_I, 1);
                while (expression[start_I] != ')')
                {
                    start_I++;
                }

                expression.Remove(start_I, 1);
            }

            return expression;
        }

        private static int SearchSign(char sign, int startIndex, StringBuilder expression)
        {
            int resultIndex = 0;
            while (expression[startIndex] != ')' && startIndex < expression.Length)
            {
                bool check = expression[startIndex - 1] == '*' || expression[startIndex - 1] == '/' || expression[startIndex - 1] == '+' || expression[startIndex - 1] == '-' || expression[startIndex - 1] == ',' || expression[startIndex - 1] == '(';
                if (expression[startIndex] == sign && !check)
                {
                    resultIndex = startIndex;
                    break;
                }

                startIndex++;
            }

            return resultIndex;
        }
    }
}
